/*
 * Copyright (c) 2009 LastFM.Desktop Anton Pavlik.
 * All Rights Reserved.
 * 
 * You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, 
 * with or without modifications, and in Source or Object form, provided that You meet the following 
 * conditions:
 * 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
 *
 * 2. You must cause any modified files to carry prominent notices stating that You changed the files; 
 * and
 *
 * 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, 
 * patent, trademark, and attribution notices from the Source form of the Work, excluding those notices 
 * that do not pertain to any part of the Derivative Works.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * You may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, Licensor provides the Work 
 * (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
 * CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties 
 * or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 
 * YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING OR REDISTRIBUTING THE WORK
 * AND ASSUME ANY RISKS ASSOCIATED WITH YOUR EXERCISE OF PERMISSIONS UNDER THIS LICENSE. 
 */
package org.shake.lastfm.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.swing.SwingWorker;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bushe.swing.event.EventBus;
import org.shake.lastfm.Constants;
import org.shake.lastfm.Parser;
import org.shake.lastfm.data.MD5;
import org.shake.lastfm.event.EventTopic;
import org.shake.lastfm.event.SessionStartFailedEvent;
import org.shake.lastfm.event.SessionStartedEvent;
import org.shake.lastfm.model.Playlist;
import org.shake.lastfm.model.UserPreferences;

public class LastFmCommunicator
{
    private Log log = LogFactory.getLog(LastFmCommunicator.class);
    private LastFmSession session;
    
    private static LastFmCommunicator instance; 
    
    private LastFmCommunicator()
    {
    }
    
    public synchronized static LastFmCommunicator getInstance()
    {
        if (instance == null)
        {
            instance = new LastFmCommunicator();
        }
        
        return instance;
    }

    private LastFmSession startSession() throws HttpEngineException, IOException
    {
        HttpEngine engine = HttpEngine.getInstance();
        LastFmSession session = new LastFmSession();

        InputStream in = engine.executeGet(buildSessionUrl());

        InputStreamReader isr = new InputStreamReader(in);
        BufferedReader reader = new BufferedReader(isr);

        String line;
        String[] parts;
        try
        {
            while ((line = reader.readLine()) != null)
            {
                parts = line.split("=", 2);

                if ("session".equals(parts[0]) && !"FAILED".equals(parts[1].toUpperCase()))
                {
                    session.setId(parts[1]);
                } else if ("stream_url".equals(parts[0]))
                {
                    session.setStreamUrl(parts[1]);
                } else if ("subscriber".equals(parts[0]))
                {
                    session.setSubscriber(Integer.valueOf(parts[1]) == 1);
                } else if ("base_url".equals(parts[0]))
                {
                    session.setBaseUrl(parts[1]);
                } else if ("base_path".equals(parts[0]))
                {
                    session.setBasePath(parts[1]);
                }
            }
        } finally
        {
            reader.close();
        }

        return session;
    }

    public synchronized void startSessionIfNeeded(final boolean invalidate)
    {
        if (invalidate)
        {
            log.info("Session is going to be invalidated");
        }

        if (invalidate || this.session == null)
        {
            new SwingWorker<LastFmSession, Void>()
            {
                @Override
                protected LastFmSession doInBackground() throws Exception
                {
                    try
                    {
                        return startSession();
                    } catch (HttpEngineException e)
                    {
                        log.error("Could not start session: ", e);
                    } catch (IOException e)
                    {
                        log.error("Could not start session: ", e);
                    }
                    return null;
                }
                @Override
                protected void done()
                {
                    LastFmSession s = null;
                    String failureReason = null;
                    try
                    {
                        s = get();
                        LastFmCommunicator.this.session = s;
                        if (LastFmCommunicator.this.session.getId() == null)
                        {
                            log.error("Could not retrieve session id");
                        }
                        else
                        {
                            log.info("Session started");
                        }
                    } catch (Exception e)
                    {
                        log.error("Could not start session: ", e);
                        failureReason = e.getMessage();
                    }
                    finally
                    {
                        if (s == null)
                        {
                            EventBus.publish(EventTopic.SESSION_START_FAILED_EVENT, new SessionStartFailedEvent(failureReason));
                        }
                        else
                        {
                            EventBus.publish(EventTopic.SESSION_STARTED_EVENT, new SessionStartedEvent(LastFmCommunicator.this.session, invalidate));
                        }
                    }
                }
            }.execute();
        }
    }

    public Playlist retrievePlaylist() throws IllegalStateException
    {
        Parser plParser;
        String plUrl = null;

        try
        {
            synchronized (this.session )
            {
                plUrl = buildPlaylistUrl();
                InputStream executeGet = HttpEngine.getInstance().executeGet( plUrl );
                plParser = new Parser(executeGet);
            }

            Playlist playlist = plParser.parse();
            return playlist;
        } catch (HttpEngineException e)
        {
            log.error("HttpEngineException: Could not retrieve playlist ("+plUrl+"): ", e);
        } 
        catch (IllegalStateException e)
        {
            log.error("IllegalStateException : Could not retrieve playlist ("+plUrl+"): ", e);
            throw new IllegalStateException(e);
        }
        catch (Exception e)
        {
            log.error("Could not retrieve playlist ("+plUrl+"): ", e);
            e.printStackTrace();
        }

        return null;
    }
    
    
    public void changeStation(String stationUrl)
    {
        //TODO parse response
        try
        {
            log.info("trying to change station to: " + stationUrl);
            String responseString = HttpEngine.getInstance().executeGetString( buildChangeStationUrl(stationUrl) );
            if (responseString.startsWith("response=OK"))
            {
                log.error("Unable to change station: " + responseString);
            }
        } catch (IllegalStateException e)
        {
            log.error(e.getMessage());
        } catch (HttpEngineException e)
        {
            log.error(e.getMessage());
        }
    }
    
    private String buildChangeStationUrl(String lastFmUrl)
    {
        StringBuffer sb = new StringBuffer();
        sb.append("http://").append(this.session.getBaseUrl()).
            append(this.session.getBasePath()).append("/adjust.php?session=")
        .append(this.session.getId()).append("&url=").append(lastFmUrl);
        
        return sb.toString();
    }
    
    private synchronized String buildPlaylistUrl()
    {
        StringBuffer sb = new StringBuffer();
        sb.append("http://").append(this.session.getBaseUrl()).append(this.session.getBasePath()).append("/xspf.php?sk=")
            .append(this.session.getId()).append("&discovery=0&desktop=1.5.1");
        
        return sb.toString();
    }

    private synchronized String buildSessionUrl()
    {
        StringBuffer sb = new StringBuffer();
        sb.append(Constants.SCROBBLER_URL).append("?").
            append(Constants.PARAM_VERSION).append("&").
            append(Constants.PARAM_PLATFORM).append("&").
            append(Constants.PARAM_USERNAME).append(UserPreferences.getInstance().getUserName()).append("&").
            append(Constants.PARAM_PASSWD).append(MD5.getMD5(UserPreferences.getInstance().getPasswd())).
            append("&").append(Constants.PARAM_DEBUG);
        return sb.toString();
    }

}